/*
 Developer: John Archer (john.archer.jpa@gmail.com)
 
 This file is part of VTBuilder.

 VTBuilder is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License.

 VTBuilder is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with VTBuilder.  If not, see <http://www.gnu.org/licenses/>.
 */
package core.tasks.pack;

import core.para.Para;
import core.processor.Net;
import core.processor.TBuilder;
import core.rb.MB;
import core.sb.Node;
import core.sb.SB;
import core.sb.s.Seq;
import gui.processor.Processor;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;

public class TScriptPack extends Thread {

    private MB pIn;
    private final CountDownLatch stopLatch;
    public SB pOut;
    private Processor p;

    public TScriptPack(CountDownLatch stopLatch, MB pIn, Processor p) {
        this.pIn = pIn;
        //give eahc of these a unique index
        for (int i = 0; i < this.pIn.mrl.size(); i++) {
            this.pIn.mrl.get(i).i = i;
        }

        this.stopLatch = stopLatch;
        this.p = p;
    }

    public void transcripts() {
        pOut = new SB();
        if (pIn.mrl.size() > 10) {
            ArrayList<TBuilder> tb = tScripts(net(pIn, pIn.refLen), pIn);
            for (TBuilder tScript : tb) {
                pOut.seqs.add(new Seq(tScript.dna));
            }
        }
        pIn = null;
    }

    public void run() {
        transcripts();
        stopLatch.countDown();
    }

    private ArrayList<TBuilder> tScripts(Net net, MB pIn) {
        if (net != null) {
            ArrayList<TBuilder> tb = new ArrayList<TBuilder>();
            ArrayList<TBuilder> storeAdd = new ArrayList<TBuilder>();
            //we want this one to go backwards as we start at right hand side
            for (int l = net.nodes().size() - 1; l > 0; l--) {
                for (TBuilder tScript : tb) {
                    tScript.added = false;
                    for (Node node : net.nodes().get(l)) {
                        for (Node kid : node.kids) {
                            if (tScript.lstNode == kid) {
                                if (getISim(node, tScript.sndLstNode)) {
                                    storeAdd.add(
                                            new TBuilder(
                                            node.dnaRep + "" + tScript.dna,
                                            node,
                                            tScript.lstNode));
                                    tScript.added = node.added = true;
                                }
                            }
                        }
                    }
                }
                for (Node node : net.nodes().get(l)) {
                    if (!node.added) {
                        storeAdd.add(
                                new TBuilder(
                                node.dnaRep,
                                node));
                    }
                }
                for (int t = tb.size() - 1; t >= 0; t--) {
                    if (tb.get(t).added) {
                        tb.remove(t);
                    }
                }
                tb.addAll(storeAdd);
                storeAdd.clear();
            }
            for (int x = tb.size() - 1; x >= 0; x--) {
                if (tb.get(x).dna.length() < Para.MNTLEN) {
                    tb.remove(x);
                }
            }
            String fullRead;
            for (int x = 0; x < tb.size(); x++) {
                fullRead = pIn.mrl.get(tb.get(x).lstNode.rIs.get(0)).dna;
                tb.get(x).dna = fullRead.substring(0, fullRead.indexOf(tb.get(x).lstNode.dnaRep)) + tb.get(x).dna;
            }
            return tb;
        }
        return null;
    }

    //have less edges and more nodes - this will help to keep variation in transcript walks
    private Net net(MB pIn, int refLen) {
        int netWin = (int) (Para.MNRL / 3);
        Net net = new Net();
        ArrayList<Node> prv = null;
        Node rt = new Node("", -1);
        ArrayList<Node> root = new ArrayList<Node>();
        root.add(rt);
        net.addNodes(root);
        ArrayList<Node> cur;
        int ht = (int) (netWin * 0.3);
        for (int w = 0; w < (refLen - netWin); w += netWin) {
            cur = getNodes(pIn.sb(w, w + netWin), ht);
            net.addNodes(cur);
            if (w == 0) {
                for (Node c : cur) {
                    rt.kids.add(c);
                }
            } else if (w > 0) {
                for (Node n : prv) {
                    for (Node c : cur) {
                        if (getISim(n, c)) {
                            n.kids.add(c);
                        }
                    }
                }
            }
            prv = cur;
        }
        return net;
    }

    private ArrayList<Node> getNodes(SB sb, int hT) {
        ArrayList<Node> clusters = new ArrayList<Node>();
        Node clst;
        for (int s1 = 0; s1 < sb.seqs.size(); s1++) {
            clst = new Node(sb.seqs.get(s1).dna, sb.seqs.get(s1).i);
            for (int s2 = sb.seqs.size() - 1; s2 > s1; s2--) {
                if (getHm(sb.seqs.get(s1).dna, sb.seqs.get(s2).dna) <= hT) {
                    clst.rIs.add(sb.seqs.remove(s2).i);
                }
            }
            if (clst.rIs.size() > 1) {
                clusters.add(clst);
            }
        }
        return clusters;
    }

    public int getHm(String s1, String s2) {
        int sum = 0;
        for (int c = 0; c < s1.length(); c++) {
            sum += ((s1.charAt(c) == s2.charAt(c)) ? 0 : 1);
        }
        return sum;
    }

    private boolean getISim(Node c1, Node c2) {
        for (int i = 0; i < c1.rIs.size(); i++) {
            for (int j = 0; j < c2.rIs.size(); j++) {
                //System.out.println(c1.rIs.get(i).intValue() + " : " + c2.rIs.get(j).intValue());
                if (c1.rIs.get(i).intValue() == c2.rIs.get(j).intValue()) {
                    //System.out.println(true);
                    return true;
                }
            }
        }
        return false;
    }
}
